<!DOCTYPE html>
<html lang="zh-cn">
  <head>
    <meta charset="UTF-8">
    <title>Sucha's Blog - Archive for February, 2021</title>
    <meta name="generator" content="MarkdownProjectCompositor.lua">
    <meta name="author" content="Sucha">
    <meta name="keywords" content="suchang, programming, Linux, Lua">
    <meta name="description" content="Sucha's blog">
    <link rel="shortcut icon" href="../images/ico.png">
    <link rel="stylesheet" type="text/css" href="../styles/blog.css">
    <link rel="stylesheet" type="text/css" href="../styles/prism.min.css">
    <style id="site_theme"></style>
  </head>
  <body>
    <div id="body">
      <div id="text">
	   <!-- Page published by cmark-gfm begins here --><h1>Sucha's Blog ~ Archive for February, 2021</h1>
<p><a id="p0"></a></p>
<div class="date">21年2月16日 周二 18:24</div>
<h2>Swift(3) - PageEventBus</h2>
<p>春节在家，继续思考了一下业务上的一些痛点，并做了整理，趁春节期间的空闲，搞了一个改善页面内通讯，限定 UI 及业务逻辑绑定关系的库 <a href="https://github.com/lalawue/PageEventBus">PageEventBus</a>。</p>
<p>先说一下之前在业务上觉得不爽的点：</p>
<ul>
<li>页面内非常多的输入组件、点击开关，以及可改变当前页面显示内容的可选项</li>
<li>这些点击开关、可选项的变化会导致页面展示内容的变化，以及输入组件的刷新，以及精度展示的配置</li>
<li>这些点击开关、可选项、显示内容、输入组件可能处于页面内各种不同层级的 UI 中，比如可以是 child view controller 等</li>
</ul>
<p>说一下之前的处理方法，使用 RxSwift，各种不管 UI 层级多深的 binder、controlProperty、controlEvent，都透过多层 UI，传递到业务 view controller 这一层来做绑定，带来的问题，有以下几个</p>
<ul>
<li>业务 view controller 急速膨胀</li>
<li>穿透用的 UI 层级，不得不写 controlEvent、binder，但实际上是其 subviews 进行处理</li>
<li>当业务变化时，每一层涉及到的 UI、业务 view controller 都要做出修改</li>
<li>UI 绑定了业务，复用很难</li>
</ul>
<p>基于上面的问题，我有了一些思考，希望有下面这样的东西</p>
<ul>
<li>UI 跟业务分离，业务逻辑能够操作 UI，UI 独立可复用</li>
<li>具体的业务可以分割到不同的业务单元处理，接收消息，可以将处理结果传递出来</li>
<li>当业务需求变化时，希望不会因为输入、输出参数的变化，而修改 UI</li>
</ul>
<p>就想着能否用上一个页面内的总线，将参数通过总线传递，这样可以对抗业务需求上导致的参数变化，比如 view model 可以接收总线消息，也可以发送消息，有了总线后，可以不需要关心 UI 层级了，也没有参数需要透传了。</p>
<p>另外，UI 的输入、输出抽象到业务逻辑 view model 的输入、输出，可以认为 view 持有了 view model，view model 持有 unowned view 的不可变引用；对于 view controller，也可以相应的持有 page model。这样，UI 总是可以复用的，而 view model，才处理实际业务，如果多个业务本身可以抽象到更高的层级，那么显然 view model 也是可以做到的。</p>
<p>还加上改进的一点，由于上面接收、发送事件的角色都在系统展示树上，可以利用 responder chain 链，在 view didMoveToWindow 或者 controller viewDidAppear 的时机，做事件总线的绑定，意味着总线本身都不需要传递，可以自动连接上。</p>
<p>上面的一些思考，加上春节空余时间的实践，就是上面说到的 <a href="https://github.com/lalawue/PageEventBus">PageEventBus</a>。</p>
<p>一些代码上的具体实践：</p>
<ul>
<li>基类是 BlockEventAgent&lt;E,R&gt;，泛型的两个参数，一个是输入类型，一个是输出类型，可以用 enum 分类限定</li>
<li>BlockEventAgent 拥有名字，当它们接收到消息，返回处理结果的时候，可以知道是谁返回的</li>
<li>BlockEventBus 很简单，添加 agent、以及给各个 agent 发送消息，搜集结果并返回给调用方</li>
<li>BlockViewModel&lt;V:UIView,E,R&gt; 泛型限定了具体操作的 view，创建的时候就需要输入 view 实例，其生命周期小于等于 view</li>
<li>BlockPageModel&lt;C:UIViewController,V:UIView,E,R&gt; 绑定了 view controller，可以选择生成 event bus，或者使用已有的</li>
<li>BlockView 和 BlockViewContnroller 都加入了在 didMoveToWindow 或 viewDidAppear 激活搜索已有 event bus 的逻辑</li>
</ul>
<p>话说这一整套限定的东西还是很多的，我准备使用 event bus 来传递 view model，view model 内部还是使用 RxSwift 来做业务绑定，view model 带 disposeBag，或者使用 view 的 disposeBag 也是可以的。</p>
<div class="category"><a href="CategoryProgramming.html">CategoryProgramming</a> / <a href="2021-02.html#p0">Permalink</a> / <a href="https://github.com/lalawue/homepage/discussions/categories/blog" target="_blank">Discussion</a></div>
<!-- Page published by cmark-gfm ends here -->
  <div id="foot">2004-<script>var d = new
	Date();document.write(d.getFullYear())</script> &copy;
	Sucha. Powered by MarkdownProjectCompositor.
  </div>
  </div><!-- text -->
  <div id="sidebar">
  </div><!-- sidebar -->
  <script src="../js/prism.min.js" async="async"></script>
  <script src="../js/blog_sidebar.js"></script>
  </div> <!-- body -->
</body>
</html>